require('../js/global.js');
var http = require('http');
var url = require('url');
var later = require('later');
var ServerManager = require('../js/servers.js').ServerManager;

global.gProcessName = 'collecter';
global.gServers = new ServerManager();
global.gStat = require('./stat/index.js');
global.logic = require('./logic');


///////////////////////////// 在线数据收集
var COLLECT_MINUTE = 58; // 每小时执行搜集程序的分钟
var COLLECT_DAY_SUM = 23; // 每天执行汇总的小时

var COLLECT_H_PAY = 0;
var COLLECT_M_PAY = 10;


main();

function main() {
    async.series([
        function (cb) {
            gServers.load({slave:true, native:true}, function(err) {
                cb(err);
            });
        },
        function (cb) {
            gStat.pool.init(function(err) {
                cb(err);
            });
        },
        function (cb) {
            gStat.cash.load(function(err) {
                cb(err);
            });
        }
    ], function(err, results) {
        if (err) {
            ERROR(util.format('start %s failed: %j', gProcessName, err));
            process.exit(-1);
        }

        var argv = process.argv;
        if (argv.length > 2) {
            doCmd(argv[2], function() {
                process.exit(0);
            });
            return;
        }
        
        LOG(util.format('%s start', gProcessName));

        gStat.cash.stat(function (err) {});

        // 设定本地时区
        later.date.localTime();
        
        var dayTimer = later.setInterval(function() {
            gStat.cash.stat(function (err) {});
        }, {
            schedules: [{h:[2], m:[30]}]
        });

        later.setInterval(function() {
            collectOnlineCount();
        }, {
            schedules: [{m:[COLLECT_MINUTE]}]
        });

        later.setInterval(function() {
            collectPay();
        }, {
            schedules: [{h:[COLLECT_H_PAY], m:[COLLECT_M_PAY]}]
        });

        later.setInterval(function() {
            gStat.game.statLogin();
        }, {
            schedules: [{h:[23], m:[25]}]
        });

        process.on('SIGINT', function() {
            onProcessExit();
        });

        process.on('SIGTERM', function() {
            onProcessExit();
        });

        process.on('uncaughtException', function (err) {  
            LOG(err.stack);        
        });
    });
};

function doCmd(cmd, callback) {
    switch (cmd) {
    case 'statLogin':
        gStat.game.statLogin(callback);
        break;
        case 'collPay':
            collectPay();
            break;
    default:
        callback();
        break;
    }
}

///////////////////////////////////////////////////////////////////////////////

/**
 * 查询指定服务器的当前在线人数
 * @param server 服务器
 * @param fn
 * @private
 */
function _queryOnline(server, fn) {
    var url = util.format('http://%s:%d?act=online', server.hostIP, server.gatewayPort);
    common.httpGet(url, function(data) {
        fn(server, +data);
    }, false);
}

/**
 * 收集用户在线信息
 */
function collectOnlineCount() {

    LOG("collectOnlineCount - start");

    async.series([
        function(cb) {
            var sids = gServers.getSids();
            var nTask = sids.length;
            var nTaskComp = 0;

            function taskFinished() {
                nTaskComp++;
                return (nTaskComp >= nTask);
            }

            var now = new Date();

            sids.forEach(function(sid){
                _queryOnline(gServers.gameServers[sid], function(server, onlineNum){
                    if (!isNaN(onlineNum)) {
                        _updateOnline(now, null, server, onlineNum, function(){
                            if (taskFinished()) {
                                cb(null);
                            }
                        });
                    } else {
                        ERROR(util.format("get online num failed: %d, %s", server.id, new Date().stdFormatedString()));
                        if (taskFinished()) {
                            cb(null);
                        }
                    }
                });
            });
        }
    ], function(err){
        LOG("collectOnlineCount - end");
    });


}

function _updateOnline(now, conn, server, onlineNum, fn) {
    var dateStamp = now.zeroTime().getTime() / 1000;

    var lastTop = 0;
    var lastAvg = 0;
    var last_UC = 0;
    var dayTotal = 0;
    var dayActive = 0;
    var dayCreate = 0;

    var collSum = false;
    var hasItem = false;
    async.series([
        function(cb) {
            gStat.pool.query(gStat.pool.format("SELECT day_online_top, day_online_avg, _uc FROM online WHERE sid=? and day=?;",
                    [server.id, dateStamp]),
                function(err, res){
                if (err) { cb(err) }
                else {
                    if (res && res.length > 0) {
                        hasItem = true;

                        var item = res[0];
                        lastTop = +(item['day_online_top']);
                        lastAvg = +(item['day_online_avg']);
                        last_UC = +(item['_uc']);
                    }
                    cb(null);
                }
            });
        },
        function(cb) {
            if (now.getHours() == COLLECT_DAY_SUM) {
                collSum = true;
                _getDaySum(now, server, function(arr){
                    if (arr) {
                        dayTotal = +arr[0];
                        dayActive = +arr[1];
                        dayCreate = +arr[2];
                        if (isNaN(dayTotal)) dayTotal = 0;
                        if (isNaN(dayActive)) dayActive = 0;
                        if (isNaN(dayCreate)) dayCreate = 0;
                    }
                    cb(null);
                });
            } else {
                cb(null);
            }
        },
        function(cb) {
            var newTop = Math.max(lastTop, onlineNum);
            var newAvg = Math.floor(((lastAvg * last_UC) + onlineNum) / (last_UC + 1));
            var newUC = last_UC + 1;
            var sql = "";
            if (!collSum) {
                sql = gStat.pool.format("UPDATE online SET day_online_top=?, day_online_avg=?, _uc=? WHERE sid=? and day=?;",
                    [newTop, newAvg, newUC, server.id, dateStamp]);
                if (!hasItem) {
                    sql = gStat.pool.format("INSERT INTO online VALUES (0, ?, ?, ?, ?, ?, ?, ?, ?);",
                        [server.id, dateStamp, 0, 0, newTop, newAvg, 0, 1]);
                }
            } else {
                sql = gStat.pool.format("UPDATE online SET " +
                    "day_act=?, " +
                    "day_act_effective=?, " +
                    "day_online_top=?, " +
                    "day_online_avg=?, " +
                    "tol_user=?, " +
                    "_uc=? WHERE sid=? and day=?;",
                    [dayActive, Math.abs(dayActive-dayCreate), newTop, newAvg, dayTotal, newUC, server.id, dateStamp]);
                if (!hasItem) {
                    sql = gStat.pool.format("INSERT INTO online VALUES (0, ?, ?, ?, ?, ?, ?, ?, ?);",
                        [server.id, dateStamp, dayActive, Math.abs(dayActive-dayCreate), newTop, newAvg, dayTotal, 1]);
                }
            }

            gStat.pool.query(sql, function(err, res){
                cb(err);
            });
        }
    ], function(err){
        if (err) {
            ERROR(util.format('%j', err));
        }
        fn();
    });
}

function _getDaySum(now, server, fn) {

    var sid = +server['id'];
    var zeroTime = now.zeroTime().getStamp();

    var db = gServers.getGameDB(sid);
    if (!db) {
        fn(); return;
    }

    var dbUser = db.collection('user');
    var dbPlat = db.collection('plat');
    var counts = {};
    var loader = new common.Loader(function() {
        fn([counts.total, counts.active, counts.create]);
    });

    loader.addLoad('total');
    loader.addLoad('active');
    loader.addLoad('create');

    dbUser.count({}, function(err, count) {
        counts.total = count;
        loader.onLoad('total');
    });

    dbUser.count({'mark.day':(now.getFullYear() * 10000 + (now.getMonth()+1) * 100 + now.getDate())}, function(err, count){
        counts.active = count;
        loader.onLoad('active');
    });

    dbPlat.count({'time':{$gt:zeroTime}}, function(err, count) {
        counts.create = count;
        loader.onLoad('create');
    });
}


///////////////////////////////////////////////////////////////////////////////

/**
 * 收集用户付费信息
 */
function collectPay() {

    LOG("collectPay - start");

    async.series([
        function(cb) {
            var sids = gServers.getSids();
            var nTask = sids.length;
            var nTaskComp = 0;

            function taskFinished() {
                nTaskComp++;
                return (nTaskComp >= nTask);
            }

            var now = new Date();
            var coll_date = new Date(util.format("%d/%d/%d 23:59:59",
                now.getFullYear(), now.getMonth() + 1, now.getDate()));
            coll_date.setDate(coll_date.getDate() - 1);
            LOG(coll_date);
            sids.forEach(function(sid){
                _updatePay(coll_date, null, gServers.gameServers[sid], function(){
                    if (taskFinished()) {
                        cb(null);
                    }
                });
            });
        }
    ], function(err){
        LOG("collectPay - end");
    });

}

function _updatePay(now, conn, server, fn) {
    var dateStamp = now.zeroTime().getTime() / 1000;

    var payCash = 0; // 今日充值金额
    var payUser = 0; // 今日充值人数
    var dayAct = 0; // 今日活跃人数
    var payNew = 0; // 今日新增付费玩家

    async.series([
        function(cb) {
            _getDayPaySum(now, server, function(arr){
                if (arr) {
                    dayAct = +arr[0];
                    payUser = +arr[1];
                    payCash = +arr[2];
                    payNew = +arr[3];
                    if (isNaN(dayAct)) dayAct = 0;
                    if (isNaN(payUser)) payUser = 0;
                    if (isNaN(payCash)) payCash = 0;
                    if (isNaN(payNew)) payNew = 0;
                }
                cb(null);
            });
        },
        function(cb) {
            var sql = gStat.pool.format("INSERT INTO pay VALUES (0, ?, ?, ?, ?, ?, ?);",
                [server.id, dateStamp, parseInt(payCash * config.platform.exchange_rate), payUser, dayAct, payNew]);

            gStat.pool.query(sql, function(err, res){
                cb(err);
            });
        }
    ], function(err){
        if (err) {
            ERROR(util.format('%j', err));
        }
        fn();
    });
}

function _getDayPaySum(now, server, fn) {

    var sid = +server['id'];
    var zeroTime = now.zeroTime().getStamp();
    var nowDays = now.getDays();

    var db = gServers.getGameDB(sid);
    if (!db) {
        fn(); return;
    }

    var dbUser = db.collection('user');
    var dbPay = db.collection('pay');
    var counts = {};
    var loader = new common.Loader(function() {
        fn([counts.active, counts.payNum, counts.pay, counts.newPay]);
    });

    loader.addLoad('active');
    loader.addLoad('payNum');
    loader.addLoad('pay');
    loader.addLoad('payNew');

    // 今日活跃人数
    dbUser.count({'mark.day':(now.getFullYear() * 10000 + (now.getMonth()+1) * 100 + now.getDate())}, function(err, count){
        counts.active = count;
        loader.onLoad('active');
    });

    // 今日付费人数
    dbUser.count(
        {
            'daily_vars':{$exists:true},
            'daily_vars.day':nowDays,
            'daily_vars.pay_cash':{'$gt':0}
        },
        function(err, count) {
            counts.payNum = count;
            loader.onLoad('payNum');
        }
    );

    var minTime = +zeroTime;
    var maxTime = minTime + 86400;

    // 今日付费总金额
    dbPay.group(
        {},
        {'ts': {'$gte': minTime + '', '$lt': maxTime + ''}},
        {sum:0},
        function(obj, prev){
            prev.sum += +obj.amt;
        },
        function(err, res){
            counts.pay = 0;
            if (!err && res && res.length > 0) {
                counts.pay = +res[0]['sum'];
            }
            loader.onLoad('pay');
        }
    );

    // 今日新增付费玩家
    dbPay.group(
        {'openid':1},
        {},
        {l:99999999999, g:0, n:false, t:now.getStamp()},
        function(doc, prev) {
            prev.l = Math.min(prev.l, +doc.ts);
            prev.g = Math.max(prev.g, +doc.ts);
        },
        function(prev) {
            var now = new Date((+prev.t) * 1000);
            var minTS = Math.floor(new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime() / 1000);
            var maxTS = minTS + 86400;
            if (prev.l >= minTS && prev.g < maxTS) {
                prev.n = true;
            }
        },
        function(err, res){
            counts.newPay = 0;
            if (!err && res) {
                res.forEach(function(item){
                    if (item.n === true) {
                        counts.newPay++;
                    }
                });
            }
            loader.onLoad("payNew");
        }
    );
}
